using System;
using System.IO;
using System.Threading;
using Moq;
using NUnit.Framework;
using OpenWaves.Web.ImageTransformations;

namespace OpenWaves.UnitTests.Web.ImageTransformations
{
    [TestFixture]
    public class ConcurrentFileStoreTests
    {
        [Test]
        public void GetOrCreateFileUrl_waits_for_thread_writing_to_file_before_returning_url_to_existing_file()
        {
            var fileStoreMock = new Mock<IFileStore>();
            fileStoreMock.Setup(s => s.FileExists("testing")).Returns(false);
            fileStoreMock.Setup(s => s.CreateFile("testing")).Returns(new MemoryStream());
            fileStoreMock.Setup(s => s.GetFileUrl("testing")).Returns(Url.Parse("~/testing"));

            var concurrentFileStore = new ConcurrentFileStore(fileStoreMock.Object);
            
            var fileCreatedEvent = new ManualResetEvent(false);
            var closeStreamEvent = new ManualResetEvent(false);
            ThreadPool.QueueUserWorkItem(state => concurrentFileStore.GetOrCreateFileUrl(
                "testing",
                TimeSpan.FromSeconds(10),
                stream =>
                    {
                        fileCreatedEvent.Set();
                        closeStreamEvent.WaitOne();
                    }));

            fileCreatedEvent.WaitOne();
            fileStoreMock.Setup(s => s.FileExists("testing")).Returns(true);

            Url url = null;
            ThreadPool.QueueUserWorkItem(state => url = concurrentFileStore.GetOrCreateFileUrl("testing", TimeSpan.FromSeconds(10), stream => { }));

            Thread.Sleep(100);
            Assert.That(url, Is.Null);
            closeStreamEvent.Set();
            Thread.Sleep(100);
            Assert.That(url, Is.Not.Null);
        }

        [Test]
        [ExpectedException(typeof(TimeoutException))]
        public void GetOrCreateFileUrl_timeouts_when_waiting_for_long_running_writer()
        {
            var fileStoreMock = new Mock<IFileStore>();
            fileStoreMock.Setup(s => s.FileExists("testing")).Returns(false);
            fileStoreMock.Setup(s => s.CreateFile("testing")).Returns(new MemoryStream());
            fileStoreMock.Setup(s => s.GetFileUrl("testing")).Returns(Url.Parse("~/testing"));

            var concurrentFileStore = new ConcurrentFileStore(fileStoreMock.Object);
            
            var fileCreatedEvent = new ManualResetEvent(false);
            ThreadPool.QueueUserWorkItem(state => concurrentFileStore.GetOrCreateFileUrl(
                "testing",
                TimeSpan.FromSeconds(10),
                stream =>
                    {
                        fileCreatedEvent.Set();
                        Thread.Sleep(TimeSpan.FromSeconds(1));
                    }));

            fileCreatedEvent.WaitOne();
            fileStoreMock.Setup(s => s.FileExists("testing")).Returns(true);

            concurrentFileStore.GetOrCreateFileUrl("testing", TimeSpan.FromMilliseconds(100), stream => { });
        }
    }
}